/*
 * Copyright 2019 Flipkart Internet Pvt. Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.flipkart.ohos.proteus.processor;

import ohos.agp.components.Component;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;

import com.flipkart.ohos.proteus.DataContext;
import com.flipkart.ohos.proteus.FunctionManager;
import com.flipkart.ohos.proteus.LogUtil;
import com.flipkart.ohos.proteus.ProteusView;
import com.flipkart.ohos.proteus.value.AttributeResource;
import com.flipkart.ohos.proteus.value.Binding;
import com.flipkart.ohos.proteus.value.NestedBinding;
import com.flipkart.ohos.proteus.value.ObjectValue;
import com.flipkart.ohos.proteus.value.Primitive;
import com.flipkart.ohos.proteus.value.Resource;
import com.flipkart.ohos.proteus.value.StyleResource;
import com.flipkart.ohos.proteus.value.Value;

import java.io.IOException;

/**
 * @author kirankumar
 * @author adityasharat
 */
public abstract class AttributeProcessor<V extends Component> {
    public static Value evaluate(final Context context, final Value input, final Value data, final int index) {
        final Value[] output = new Value[1];
        AttributeProcessor<Component> processor = new AttributeProcessor<Component>() {
            @Override
            public void handleBinding(Component view, Binding binding) {
                output[0] = binding.evaluate(context, data, index);
            }

            @Override
            public void handleValue(Component view, Value value) {
                output[0] = value;
            }

            @Override
            public void handleResource(Component view, Resource resource) {
                output[0] = new Primitive(resource.getString(context));
            }

            @Override
            public void handleAttributeResource(Component view, AttributeResource attribute) {
                try {
                    output[0] = new Primitive(attribute.apply(context).getString(view.getName()));
                } catch (IOException | NotExistException | WrongTypeException e) {
                    LogUtil.debug("WRY", "IOException--->" + e.getMessage());
                    e.printStackTrace();
                }
            }

            @Override
            public void handleStyleResource(Component view, StyleResource style) {
                try {
                    output[0] = new Primitive(style.apply(context).getString(view.getName()));
                } catch (IOException | NotExistException | WrongTypeException e) {
                    e.printStackTrace();
                }
            }
        };
        //noinspection unchecked
        processor.process(null, input);
        return output[0];
    }

    protected Value evaluate(Binding binding, Context context, Value data, int index) {
        return binding.evaluate(context, data, index);
    }

    public static Value staticPreCompile(Primitive value, Context context, FunctionManager manager) {
        String string = value.getAsString();
        if (Binding.isBindingValue(string)) {
            return Binding.valueOf(string, context, manager);
        } else if (Resource.isResource(string)) {
            return Resource.valueOf(string, null, context);
        } else if (AttributeResource.isAttributeResource(string)) {
            return AttributeResource.valueOf(string, context);
        } else if (StyleResource.isStyleResource(string)) {
            return StyleResource.valueOf(string, context);
        }
        return null;
    }

    public static Value staticPreCompile(ObjectValue object, Context context, FunctionManager manager) {
        Value binding = object.get(NestedBinding.NESTED_BINDING_KEY);
        if (binding != null) {
            return NestedBinding.valueOf(binding);
        }
        return null;
    }

    public static Value staticPreCompile(Value value, Context context, FunctionManager manager) {
        Value compiled = null;
        if (value.isPrimitive()) {
            compiled = staticPreCompile(value.getAsPrimitive(), context, manager);
        } else if (value.isObject()) {
            compiled = staticPreCompile(value.getAsObject(), context, manager);
        } else if (value.isBinding() || value.isResource() || value.isAttributeResource() || value.isStyleResource()) {
            return value;
        }
        return compiled;
    }

    public void process(V view, Value value) {
        if (value.isBinding()) {
            handleBinding(view, value.getAsBinding());
        } else if (value.isResource()) {
            handleResource(view, value.getAsResource());
        } else if (value.isAttributeResource()) {
            handleAttributeResource(view, value.getAsAttributeResource());
        } else if (value.isStyleResource()) {
            handleStyleResource(view, value.getAsStyleResource());
        } else {
            handleValue(view, value);
        }
    }

    public void handleBinding(V view, Binding value) {
        DataContext dataContext = ((ProteusView) view).getViewManager().getDataContext();
        Value resolved = evaluate(value, view.getContext(), dataContext.getData(), dataContext.getIndex());
        handleValue(view, resolved);
    }

    public abstract void handleValue(V view, Value value);

    public abstract void handleResource(V view, Resource resource);

    public abstract void handleAttributeResource(V view, AttributeResource attribute);

    public abstract void handleStyleResource(V view, StyleResource style);

    public Value precompile(Value value, Context context, FunctionManager manager) {
        Value compiled = staticPreCompile(value, context, manager);
        return null != compiled ? compiled : compile(value, context);
    }

    public Value compile(Value value, Context context) {
        return value;
    }
}
